<script lang="ts" setup>
  import { Plus, Upload, ZoomIn, Download, Delete } from '@element-plus/icons-vue'
  import { ElMessage, ElMessageBox } from 'element-plus'
  import type { UploadFile, UploadRawFile, UploadRequestOptions, UploadProgressEvent, UploadFiles, UploadInstance } from 'element-plus'
  import { compressAccurately } from 'image-conversion'
  import COS from 'cos-js-sdk-v5'

  import { deleteFile, getUploadSignature } from '@/api/upload'
  import { fileLists } from '@/types/competition'
  import { useUserStore } from '@/store/modules/user'
  import { parseTime, randomizeUidRange } from '@/utils'

  // 全局缓存临时密钥
  let cachedSignature: any = null
  let signatureExpireTime: number = 0 // 时间戳（秒）
  // 获取签名（带缓存）
  const getCachedSignature = async (value: string) => {
    const now = Math.floor(Date.now() / 1000)
    // 如果缓存存在且没过期，直接用缓存
    if (cachedSignature && now < signatureExpireTime - 120) {
      return cachedSignature
    }

    // 重新获取签名
    const res = await getUploadSignature({ value })
    cachedSignature = res
    signatureExpireTime = res.expiredTime // 后端返回的过期时间（秒级）
    return res
  }
  const compressImage = async (file: File, compress: number): Promise<File> => {
    return new Promise((resolve, reject) => {
      // 检查是否为图片文件
      if (!file.type.startsWith('image/')) {
        console.log('非图片文件，跳过压缩:', file.type)
        resolve(file)
        return
      }
      // 超出多少M进行压缩
      const kb = file.size / 1024 / 1024
      if (!compress || compress >= kb) {
        resolve(file)
        return
      }
      console.log('执行图片压缩：' + kb + 'MB')
      // 压缩至1M，宽度800
      const config = {
        size: 1024,
        width: 800,
      }
      console.log('开始压缩：' + kb + 'MB')
      compressAccurately(file, config)
        .then((res: Blob) => {
          console.log('图片压缩后：' + (res.size / 1024 / 1024).toFixed(2) + 'MB')
          // 创建新的 File 对象，保留原有属性
          const compressedFile = new File([res], file.name, {
            type: res.type || file.type,
            lastModified: Date.now(),
          })
          resolve(compressedFile)
        })
        .catch(reject)
    })
  }
  // 定义 props 类型
  interface Props {
    modelValue?: fileLists[]
    accept?: string
    tips?: string
    listType?: 'text' | 'picture' | 'picture-card'
    limit?: number
    compress?: number
    autoUpload?: boolean
    drag?: boolean
    maxSize?: number // 文件大小限制，单位MB
  }

  // 定义 emits 类型
  interface Emits {
    (e: 'update:modelValue', value: fileLists[]): void
    (e: 'select', file: UploadRawFile | UploadFile): void
    (e: 'preview', file: fileLists): void
  }

  // Props 定义
  const props = withDefaults(defineProps<Props>(), {
    modelValue: () => [],
    accept: undefined,
    tips: undefined,
    listType: 'text',
    limit: 1,
    compress: 0,
    autoUpload: true,
    drag: false,
    maxSize: 1024, // 默认最大1G
  })

  // Emits 定义
  const emit = defineEmits<Emits>()

  // 响应式数据
  const fileUpload = ref<UploadInstance>()
  const fileList = ref<fileLists[]>([]) // 使用扩展的接口类型
  const cos = ref<any>(null)
  const taskIds = reactive<Record<string, string>>({}) // 改为 Map 结构，key 是 file.uid，value 是 taskId
  const dialogImageUrl = ref('')
  const dialogVisible = ref(false)
  const disabled = ref(false)
  // 获取用户store
  const userStore = useUserStore()
  const userName = userStore.userInfo?.name || userStore.userInfo?.employeeNumber || '主动生成'
  const userId = userStore.userInfo?.id || 'CDTUXXXX'
  // 监听 modelValue 变化
  watch(
    () => props.modelValue,
    (val) => {
      fileList.value = val || []
    },
    { immediate: true },
  )

  // 填充数据
  const fillValue = () => {
    fileList.value = props.modelValue || []
  }

  // 文件上传成功时的钩子
  const handleSuccess = (response: any, file: fileLists, list: fileLists[]) => {
    // 直接传递出保存后的文件列表
    // 只有一个文件 就传一个替换
    if (props.limit === 1 && fileList.value.length > 1) {
      fileList.value = [file]
    }
    console.log('上传成功:', fileList.value)
    emit('update:modelValue', fileList.value)
  }

  const beforeUpload = async (file: UploadRawFile): Promise<File | boolean> => {
    console.log('文件上传触发之前：', file)
    // 检查文件大小
    if (props.maxSize && file.size > props.maxSize * 1024 * 1024) {
      ElMessage.error(`文件大小不能超过 ${props.maxSize}MB！当前文件大小：${(file.size / 1024 / 1024).toFixed(2)}MB`)
      return false
    }

    emit('select', file)
    try {
      const compressedFile = await compressImage(file, props.compress)
      return compressedFile
    } catch (error) {
      console.error('文件压缩失败:', error)
      return false
    }
  }

  // 文件超出个数限制时的钩子
  const handleExceed = () => {
    ElMessage.warning(`每次只能上传 ${props.limit} 个文件`)
  }

  // 删除文件之前的钩子
  const beforeRemove = async (uploadFile: fileLists, uploadFiles: fileLists[]) => {
    try {
      await ElMessageBox.confirm('确定移除文件?若已上传至服务器将同步移除！', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
      ElMessage({
        type: 'success',
        message: '移除成功',
      })
      return true
    } catch {
      ElMessage({
        type: 'info',
        message: '已取消移除',
      })
      return false
    }
  }

  // 文件列表移除文件时的钩子
  const handleRemove = (file: fileLists) => {
    // 移除文件
    console.log('移除文件', file)
    const index = fileList.value.findIndex((item) => item.uid === file.uid)
    if (index > -1) {
      fileList.value.splice(index, 1)
    }
    // 如果存在 url
    if (file.url && file.url.includes('cos.ap-chengdu.')) {
      // 调用删除接口
      deleteFile({ key: file.url, value: userName + userId, uid: file.uid })
    }
    // 取消进行中的任务
    if (cos.value && file.uid && taskIds[file.uid]) {
      cos.value.cancelTask(taskIds[file.uid])
      // 清理对应的 taskId 记录
      delete taskIds[file.uid]
    }
    // 同步数据
    emit('update:modelValue', fileList.value)
  }

  // 手动上传文件
  const manualUpload = (param: UploadRequestOptions): Promise<void> => {
    return new Promise(async (resolve, reject) => {
      try {
        const originalFileName = param.file.name
        const fileExtension = originalFileName.split('.').pop()
        const baseFileName = originalFileName.substring(0, originalFileName.lastIndexOf('.'))
        const fileName = `${parseTime(Date.now(), '{y}{m}{d}')}-${baseFileName}.${fileExtension}`
        // // 根据扩展名确定路径
        let filePath = `${userName + userId}/${fileName}`
        // 使用缓存的签名
        const res = await getCachedSignature(userName + userId)
        const cos = new COS({
          getAuthorization: function (options, callback) {
            callback({
              TmpSecretId: res.credentials.tmpSecretId,
              TmpSecretKey: res.credentials.tmpSecretKey,
              XCosSecurityToken: res.credentials.sessionToken,
              StartTime: res.startTime,
              ExpiredTime: res.expiredTime,
            })
          },
        })
        cos.uploadFile(
          {
            Bucket: 'lichuyi-1330222639',
            Region: 'ap-chengdu',
            Key: filePath,
            Body: param.file,
            SliceSize: 1024 * 1024 * 15,
            ChunkSize: 1024 * 1024 * 3,
            AsyncLimit: 3,
            StorageClass: 'STANDARD',
            onProgress: (progressData) => {
              const progressEvent = {
                lengthComputable: true,
                loaded: progressData.loaded,
                total: progressData.total,
                percent: progressData.percent * 100,
              } as UploadProgressEvent
              param.onProgress(progressEvent)
            },
          },
          (err, data) => {
            if (err) {
              param.onError(err as any)
              reject(err)
            } else {
              const fileObj = {
                name: param.file.name,
                url: decodeURIComponent('https://' + data.Location),
                uid: randomizeUidRange(param.file.uid, 0, 4),
                status: 'success' as const,
              }
              fileList.value.push(fileObj)
              resolve()
            }
          },
        )
      } catch (error) {
        ElMessage.error('获取上传密钥失败！')
        param.onError(error as any)
        reject(error)
      }
    })
  }
  // 处理手动上传服务器
  const submitUpload = () => {
    fileUpload.value.submit()
  }
  // 点击选中文件
  const handlePreview = (uploadFile: fileLists) => {
    console.log('点击文件选择', uploadFile)
    emit('preview', uploadFile)
  }
  const uploadOK = () => {}
  //手动上传文件时处理上传操作
  // const fileSelected = (file: UploadFile, fileListParam: UploadFile[]) => {
  //   console.log('这里的文件？', file)
  //   emit('select', file)
  //   // 如果是单文件上传，且已经有文件了，则替换
  //   if (props.limit === 1 && fileListParam.length > 1) {
  //     fileList.value = [file]
  //   } else {
  //     fileList.value = fileListParam
  //   }
  // }
  // 生成上传提示信息
  const getUploadTips = () => {
    if (props.tips) return null // 如果有自定义提示就不显示默认提示
    let tipText = ''
    // 根据 accept 类型生成提示
    if (props.accept) {
      if (props.accept.includes('image')) {
        tipText = '支持上传图片文件'
        if (props.compress > 0) {
          tipText += `，超过${props.compress}MB将自动压缩`
        }
      } else {
        tipText = '支持上传文件类型：' + props.accept
      }
    } else {
      tipText = '支持上传任意格式文件'
    }

    // 文件大小限制提示
    if (props.maxSize) {
      tipText += `，单个文件不超过${props.maxSize}MB`
    }

    // 文件数量限制提示
    if (props.limit && props.limit > 1) {
      tipText += `，最多上传${props.limit}个文件`
    } else if (props.limit === 1) {
      tipText += `，只能上传1个文件`
    }
    return tipText
  }

  const handlePictureCardPreview = (file: fileLists) => {
    dialogImageUrl.value = file.url!
    dialogVisible.value = true
  }
  const handleDownload = (file: fileLists) => {
    console.log('开始下载文件:', file)
    if (file.url) {
      // 使用分片下载
      downloadFileInChunks(file.url, file.name)
    }
  }

  // 分片下载相关函数
  const getFileSize = async (url: string): Promise<number> => {
    try {
      const response = await fetch(url, { method: 'HEAD' })
      const size = parseInt(response.headers.get('content-length') || '0')
      return size
    } catch (error) {
      console.error('获取文件大小失败:', error)
      throw error
    }
  }

  const downloadChunk = async (url: string, start: number, end: number): Promise<ArrayBuffer> => {
    try {
      const response = await fetch(url, {
        headers: {
          Range: `bytes=${start}-${end}`,
        },
      })
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }
      return response.arrayBuffer()
    } catch (error) {
      console.error(`下载分片失败 (${start}-${end}):`, error)
      throw error
    }
  }

  const downloadFileInChunks = async (url: string, filename: string, chunkSize: number = 2 * 1024 * 1024) => {
    try {
      ElMessage.info('开始下载文件...')
      // 1. 获取文件总大小
      const fileSize = await getFileSize(url)
      console.log(`文件大小: ${(fileSize / 1024 / 1024).toFixed(2)}MB`)
      // 2. 如果文件小于分片大小，直接下载
      if (fileSize <= chunkSize) {
        const a = document.createElement('a')
        a.href = url
        a.download = filename
        a.click()
        ElMessage.success('下载完成')
        return
      }
      // 3. 计算分片数量
      const totalChunks = Math.ceil(fileSize / chunkSize)
      console.log(`总分片数: ${totalChunks}`)
      // 4. 并发下载所有分片（限制并发数）
      const downloadPromises = []
      const maxConcurrency = 3 // 最大并发数
      for (let i = 0; i < totalChunks; i++) {
        const start = i * chunkSize
        const end = Math.min(start + chunkSize - 1, fileSize - 1)

        downloadPromises.push(
          downloadChunk(url, start, end).then((chunk) => ({
            index: i,
            data: chunk,
          })),
        )
        // 控制并发数
        if (downloadPromises.length >= maxConcurrency) {
          await Promise.all(downloadPromises.splice(0, maxConcurrency))
        }
      }
      // 5. 等待剩余分片下载完成
      const results = await Promise.all(downloadPromises)
      // 6. 按顺序合并分片
      results.sort((a, b) => a.index - b.index)
      const mergedBuffer = new Uint8Array(fileSize)
      let offset = 0

      results.forEach((result) => {
        const chunk = new Uint8Array(result.data)
        mergedBuffer.set(chunk, offset)
        offset += chunk.length
      })

      // 7. 创建下载链接
      const blob = new Blob([mergedBuffer])
      const downloadUrl = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = downloadUrl
      a.download = filename
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
      URL.revokeObjectURL(downloadUrl)

      ElMessage.success('分片下载完成')
    } catch (error) {
      console.error('分片下载失败:', error)
      ElMessage.error('下载失败，请重试')

      // 降级到普通下载
      try {
        const a = document.createElement('a')
        a.href = url
        a.download = filename
        a.click()
        ElMessage.info('已切换到普通下载模式')
      } catch (fallbackError) {
        console.error('普通下载也失败:', fallbackError)
      }
    }
  }
  // 生命周期
  onMounted(() => {
    fillValue()
  })
</script>
<template>
  <div class="content">
    <el-upload
      ref="fileUpload"
      :http-request="manualUpload"
      :before-remove="beforeRemove"
      :before-upload="beforeUpload"
      :on-success="handleSuccess"
      :on-remove="handleRemove"
      :on-exceed="handleExceed"
      :on-preview="handlePreview"
      :drag="props.drag"
      :accept="props.accept"
      :multiple="props.limit > 1"
      :limit="props.limit === 1 ? 2 : props.limit"
      :file-list="fileList"
      :list-type="props.listType"
      action=""
      :auto-upload="props.autoUpload"
    >
      <!-- 图片缩略图 -->
      <template v-if="props.listType === 'picture-card'" #file="{ file }">
        <div>
          <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
          <span class="el-upload-list__item-actions">
            <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
              <el-icon><zoom-in /></el-icon>
            </span>
            <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
              <el-icon><Download /></el-icon>
            </span>
            <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
              <el-icon><Delete /></el-icon>
            </span>
          </span>
        </div>
      </template>
      <!-- 上传按钮 -->
      <template v-if="props.listType === 'text'" #trigger>
        <el-button type="primary">选择文件</el-button>
      </template>
      <!-- 手动上传按钮 -->
      <el-button v-if="!props.autoUpload" style="margin-left: 20px" class="ml-3" type="success" @click="submitUpload">
        上传到服务器
      </el-button>
      <span v-if="!props.autoUpload" style="margin-left: 15px">当前为手动上传，请在选择文件后点击上传按钮</span>
      <!-- 提示信息 -->
      <template #tip>
        <div class="el-upload__tip">
          <span v-if="props.tips">{{ props.tips }}</span>
          <span v-else>{{ getUploadTips() }}</span>
        </div>
      </template>
    </el-upload>
  </div>
</template>

<style scoped lang="scss"></style>
